/*
 * Merge.cpp
 *
 *  Created on: Oct 8, 2012
 *      Author: romina
 */

#include "Merge.h"

namespace ordenamiento {


Merge::Merge(string dir_fusiones, string dir_particiones, int cant_part) {
	this->file_log = new ArchivoDeTexto("informe_merge.txt", W);
	this->cant_part = cant_part;
	this->particiones = NULL;
	this->qbuffers = NULL;
	this->path_fusiones = dir_fusiones + "/";
	this->path_particiones = dir_particiones + "/";
	this->bufferFusion = new Buffer(TAM_BUFFER_FUSION);
	this->path_archivo_ordenado = "";
}


int Merge::cargar_particiones(int num, string ruta){
	this->particiones = new Particion[MAX_PARTICIONES_OPEN];
	int cantidad = 0;
	for(int i = 0; i < MAX_PARTICIONES_OPEN; i++){
		if (i + num == this->cant_part)
			break;
		string id = Palabra::intToString(i + num);
		this->particiones[i].set(ruta+id+".bin", i + num);
		this->particiones[i].open();

		cantidad++;
	}
	return cantidad;
}



void Merge::cargar_buffers(){
	this->qbuffers = new QBuffer[CANT_BUFFERS_FUSION];
	for(int i = 0; i < CANT_BUFFERS_FUSION; i++){
		this->qbuffers[i].setCapacity(TAM_BUFFER_FUSION);
	}
}


void Merge::llenar_buffer_con_particion(int numero){
	string dato;
	for(int i = 0; i < TAM_BUFFER_FUSION; i++){

		if(!this->particiones[numero].is_EOF()){
			dato = this->particiones[numero].obtenerLinea();
			RecordTermino rec = RecordTermino(dato);
			this->qbuffers[numero].pushRecord(rec);
		}
		else{
			RecordTermino rec = RecordTermino(CLAVE_IMPOSIBLE);
			this->qbuffers[numero].pushRecord(rec);
			break;
		}

	}
}

void Merge::llenar_buffer_con_imposibles(int pos_buffer){
	RecordTermino rec = RecordTermino(CLAVE_IMPOSIBLE);
	this->qbuffers[pos_buffer].pushRecord(rec);

}

bool Merge::fin_de_archivos(){
	RecordTermino rec_imp = RecordTermino(CLAVE_IMPOSIBLE);
	for(int i = 0; i < CANT_BUFFERS_FUSION; i++){
		if (this->qbuffers[i].getRecord().compare(rec_imp) != 0){
			return false;
		}
	}
	return true;
}

int Merge::pendientes_fusion(){
	int tamanio = 0;
	for(int i = 0; i < CANT_BUFFERS_FUSION; i++){
		tamanio = tamanio + this->qbuffers[i].size();
	}
	return tamanio - CANT_BUFFERS_FUSION;
}

string Merge::get_path_archivo_ordenado(){
	return this->path_archivo_ordenado;
}

void Merge::merge(){

	if (this->cant_part == 0){
		throw NoSeEncontraronParticionesException();
	}

	int particiones = 0;
	int etapa = 1;
	int cant_fusion = 0;
	ArchivoBinario * fusion = NULL;
	this->file_log->escribirLinea("MERGE DE PARTICIONES");
	this->file_log->escribirLinea("====================");
	this->file_log->escribirLinea("");
	this->file_log->escribirLinea("ETAPA 1");

	Directorio::crear_carpeta("", this->path_fusiones);
	Directorio::crear_carpeta(this->path_fusiones, "etapa" + Palabra::intToString(etapa));


	while(particiones < this->cant_part){

		int cant = this->cargar_particiones(particiones, this->path_particiones + "particion");
		this->cargar_buffers();

		for(int i = 0; i < CANT_BUFFERS_FUSION - 1; i++){
			if (i < cant)
				this->llenar_buffer_con_particion(i);
			else
				this->llenar_buffer_con_imposibles(i);
		}
		//el ultimo siempre lleva el imposible
		this->llenar_buffer_con_imposibles(CANT_BUFFERS_FUSION - 1);

		this->file_log->escribirLinea("-------------------");
		this->file_log->escribirLinea("particion numero " + Palabra::intToString(cant_fusion));
		this->path_archivo_ordenado = this->path_fusiones + "etapa" + Palabra::intToString(etapa) + "//archivo" + Palabra::intToString(cant_fusion) + ".bin";
		fusion = new ArchivoBinario(this->path_archivo_ordenado , W);
		this->merge_intermedio(fusion);
		delete(fusion);
		particiones += cant;

		cant_fusion++;
	}



	while(cant_fusion > 1){

		this->cant_part = cant_fusion;
		etapa++;
		Directorio::crear_carpeta(this->path_fusiones, "etapa" + Palabra::intToString(etapa));

		this->file_log->escribirLinea("");
		this->file_log->escribirLinea("ETAPA " + Palabra::intToString(etapa));

		particiones = 0;
		cant_fusion = 0;
		while(particiones < this->cant_part){

				int cant = this->cargar_particiones(particiones, this->path_fusiones+"etapa"+ Palabra::intToString(etapa-1)+"//archivo");
				this->cargar_buffers();

				for(int i = 0; i < CANT_BUFFERS_FUSION - 1; i++){
					if (i < cant)
						this->llenar_buffer_con_particion(i);
					else
						this->llenar_buffer_con_imposibles(i);
				}

				//el ultimo siempre lleva el imposible
				this->llenar_buffer_con_imposibles(CANT_BUFFERS_FUSION - 1);

				this->file_log->escribirLinea("-------------------");
				this->file_log->escribirLinea("particion numero " + Palabra::intToString(cant_fusion));
				this->path_archivo_ordenado = this->path_fusiones + "etapa" + Palabra::intToString(etapa) + "//archivo" + Palabra::intToString(cant_fusion) + ".bin";

				fusion = new ArchivoBinario(this->path_archivo_ordenado, W);
				this->merge_intermedio(fusion);
				delete(fusion);
				particiones += cant;
				cant_fusion++;
			}


	}

}

void Merge::transferir_buffer_de_fusion(ArchivoBinario * fusion, bool is_last){
   for (int i = 0; i < this->bufferFusion->getCantidad(); i++){
		if(is_last && i == this->bufferFusion->getCantidad() - 1)
			fusion->escribirPalabra(this->bufferFusion->getDato(i));
		else
			fusion->escribirLinea(this->bufferFusion->getDato(i));

	}
	this->bufferFusion->vaciar();
}

void Merge::merge_intermedio(ArchivoBinario * fusion){

	RecordTermino rec_imp = RecordTermino(CLAVE_IMPOSIBLE);
	RecordTermino rec_min = RecordTermino(CLAVE_IMPOSIBLE);
	int buff_min = -1;
	int total = 0;


	while(!this->fin_de_archivos()){

		for(int i = 0; i < CANT_BUFFERS_FUSION; i++){
			RecordTermino rec = this->qbuffers[i].getRecord();
			if(rec.compare(rec_min) == -1){
					rec_min = rec;
					buff_min = i;
			}
		}

		if (buff_min == -1) break;

		RecordTermino rec3 = this->qbuffers[buff_min].getRecord();
		if (rec3.compare(rec_imp) == -1){
			if (this->bufferFusion->getCantidad() == this->bufferFusion->getCapacidad())
					this->transferir_buffer_de_fusion(fusion, false);
			this->bufferFusion->addDato(rec3.getId_Termino());

			total++;
			this->qbuffers[buff_min].popRecord();
		}

		if (this->qbuffers[buff_min].empty()){
			this->llenar_buffer_con_particion(buff_min);
		}
		rec_min = rec_imp;
		buff_min = -1;

	}
	this->transferir_buffer_de_fusion(fusion, true);
	this->file_log->escribirLinea("registros: " + Palabra::intToString(total));

}

Merge::~Merge() {
	delete(this->file_log);
}


} /* namespace ordenamiento */
